namespace HZY.Core.Redis.Aop.Cache;

/// <summary>
/// 缓存AOP
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public class CacheableAttribute : BaseCacheAttribute
{
    /// <summary>
    /// 缓存时长 (秒) 默认存储 10s 如果0代表永久
    /// </summary>
    /// <value></value>
    public long CacheDuration { get; set; } = 10;

    /// <summary>
    /// Before
    /// </summary>
    /// <param name="aopContext"></param>
    public override void Before(AopContext aopContext)
    {
        if (RedisServiceType == null)
        {
            GetMemoryCache(aopContext);
            return;
        }

        GetRedisCache(aopContext);
    }

    /// <summary>
    /// Before~TResult
    /// </summary>
    /// <param name="aopContext"></param>
    /// <typeparam name="TResult"></typeparam>
    public override void Before<TResult>(AopContext aopContext)
    {
        if (RedisServiceType == null)
        {
            GetMemoryCache(aopContext);
            return;
        }

        GetRedisCache<TResult>(aopContext);
    }

    /// <summary>
    /// After
    /// </summary>
    /// <param name="aopContext"></param>
    public override void After(AopContext aopContext)
    {
        if (aopContext.Invocation.ReturnValue == null)
        {
            return;
        }

        CreateCache(aopContext, aopContext.Invocation.ReturnValue);
    }

    /// <summary>
    /// After TResult
    /// </summary>
    /// <param name="aopContext"></param>
    /// <param name="result"></param>
    /// <typeparam name="TResult"></typeparam>
    public override void After<TResult>(AopContext aopContext, TResult result)
    {
        if (aopContext.Invocation.ReturnValue == null)
        {
            return;
        }

        CreateCache(aopContext, result);
    }

    /// <summary>
    /// 获取 MemoryCache 缓存
    /// </summary>
    /// <param name="aopContext"></param>
    private void GetMemoryCache(AopContext aopContext)
    {
        var key = GetCacheKey(aopContext);
        var memoryCache = aopContext.ServiceProvider.GetRequiredService<IMemoryCache>();
        aopContext.Invocation.ReturnValue = memoryCache.Get(key);
    }

    /// <summary>
    /// 获取 RedisCache 缓存
    /// </summary>
    /// <param name="aopContext"></param>
    private void GetRedisCache(AopContext aopContext)
    {
        var key = GetCacheKey(aopContext);
        var redisCache = GetDatabase(aopContext);
        var value = redisCache.StringGet(key);
        aopContext.Invocation.ReturnValue = string.IsNullOrWhiteSpace(value) ? null : JsonConvert.DeserializeObject(value, aopContext.Invocation.Method.ReturnType);
    }

    /// <summary>
    /// 获取 RedisCache 缓存
    /// </summary>
    /// <param name="aopContext"></param>
    private void GetRedisCache<TResult>(AopContext aopContext)
    {
        var key = GetCacheKey(aopContext);
        var redisCache = GetDatabase(aopContext);
        var value = redisCache.StringGet(key);
        aopContext.Invocation.ReturnValue = string.IsNullOrWhiteSpace(value) ? null : JsonConvert.DeserializeObject<TResult>(value);
    }

    /// <summary>
    /// 创建缓存
    /// </summary>
    /// <param name="aopContext"></param>
    /// <param name="result"></param>
    private void CreateCache(AopContext aopContext, object result)
    {
        if (RedisServiceType == null)
        {
            CreateMemoryCache(aopContext, result);
            return;
        }

        CreateRedisCache(aopContext, result);
    }

    /// <summary>
    /// 创建内存缓存
    /// </summary>
    /// <param name="aopContext"></param>
    /// <param name="result"></param>
    private void CreateMemoryCache(AopContext aopContext, object result)
    {
        var memoryCache = aopContext.ServiceProvider.GetRequiredService<IMemoryCache>();
        var key = GetCacheKey(aopContext);

        if (CacheDuration <= 0)
        {
            memoryCache.Set(key, result);
        }
        else
        {
            memoryCache.Set(key, result, TimeSpan.FromSeconds(CacheDuration));
        }
    }

    /// <summary>
    /// 创建Redis缓存
    /// </summary>
    /// <param name="aopContext"></param>
    /// <param name="result"></param>
    private void CreateRedisCache(AopContext aopContext, object result)
    {
        var redisCache = GetDatabase(aopContext);
        var key = GetCacheKey(aopContext);

        if (CacheDuration <= 0)
        {
            redisCache.StringSet(key, JsonConvert.SerializeObject(result));
        }
        else
        {
            redisCache.StringSet(key, JsonConvert.SerializeObject(result), TimeSpan.FromSeconds(CacheDuration));
        }
    }
}